﻿using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Disposables;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using Collab.Collab_Modules_VSPackage.Views;
using Collab.Common;
using Collab.Common.Infrastructure;
using Collab.Modules.Interfaces.Session;
using Collab.Modules.Session;
using Collab.Services.Remote.Entities;
using Microsoft.Practices.Composite.Presentation.Commands;
using System.Windows.Forms;

namespace Collab.Modules.Editor.ViewModels
{
    public class CollaborationControlViewModel : ObservableObject, IDisposable
    {
        private readonly CompositeDisposable disposable = new CompositeDisposable();
        private readonly CollaborationSessionFactory sessionFactory;

        private ICollaborationSession session;

        private CollaborationControlState state;
        public CollaborationControlState State
        {
            get
            {
                return this.state;
            }
            set
            {
                if (this.state == value)
                    return;
                this.state = value;
                NotifyPropertyChanged("State");
                this.IsInputEnabled = (value == CollaborationControlState.Idle);                
            }
        }

        private string name;
        public string Name
        {
            get
            {
                return this.name;
            }
            set
            {
                if (this.name == value)
                    return; ;
                this.name = value;
                NotifyPropertyChanged("Name");       
            }
        }

        private string ip;
        public string IP
        {
            get
            {
                return this.ip;
            }
            set
            {
                if (this.ip == value)
                    return;
                
                this.ip = value;
                NotifyPropertyChanged("IP");
            }
        }
        
        private bool isInputEnabled = true;
        public bool IsInputEnabled
        {
            get { return this.isInputEnabled; }
            set 
            {
                if (this.isInputEnabled == value)
                    return;
                this.isInputEnabled = value;
                NotifyPropertyChanged("IsInputEnabled");                
            }
        }

        private bool canJoin;
        public bool CanJoin
        {
            get { return this.canJoin; }
            set
            {
                if (this.canJoin == value)
                    return;
                this.canJoin = value;
                NotifyPropertyChanged("CanJoin");                
            }
        }

        private bool canHost;
        public bool CanHost
        {
            get { return this.canHost; }
            set
            {
                if (this.canHost == value)
                    return;
                this.canHost = value;
                NotifyPropertyChanged("CanHost");                
            }
        }

        private bool canLeave;
        public bool CanLeave
        {
            get { return this.canLeave; }
            set
            {
                if (this.canLeave == value)
                    return;
                this.canLeave = value;
                NotifyPropertyChanged("CanLeave");
            }
        }

        private ObservableCollection<User> users;
        public ObservableCollection<User> Users
        {
            get { return this.users; }
            set 
            {
                if (this.users == value)
                    return;
                this.users = value;
                NotifyPropertyChanged("Users");
            }
        }


        private ICommand joinCommand;
        public System.Windows.Input.ICommand JoinCommand
        {
            get
            {
                return this.joinCommand;
            }
            set
            {
                if (this.joinCommand == value)
                    return;
                this.joinCommand = value;
                NotifyPropertyChanged("JoinCommand");
                
            }
        }

        private ICommand hostCommand;
        public System.Windows.Input.ICommand HostCommand
        {
            get
            {
                return this.hostCommand;
            }
            set
            {
                if (this.hostCommand == value)
                    return;
                this.hostCommand = value;
                NotifyPropertyChanged("HostCommand");                
            }
        }

        private ICommand navigateToCommand;
        public System.Windows.Input.ICommand NavigateToCommand
        {
            get
            {
                return this.navigateToCommand;
            }
            set
            {
                if (this.navigateToCommand == value)
                    return;
                this.navigateToCommand = value;
                NotifyPropertyChanged("NavigateToCommand");
            }
        }

        private ICommand leaveCommand;
        public System.Windows.Input.ICommand LeaveCommand
        {
            get
            {
                return this.leaveCommand;
            }
            set
            {
                if (this.leaveCommand == value)
                    return;
                this.leaveCommand = value;
                NotifyPropertyChanged("LeaveCommand");
            }
        }

        private ICommand printDebugCommand;
        public System.Windows.Input.ICommand PrintDebugCommand
        {
            get
            {
                return this.printDebugCommand;
            }
            set
            {
                if (this.printDebugCommand == value)
                    return;
                this.printDebugCommand = value;
                NotifyPropertyChanged("PringDebugCommand");
            }
        }

        public CollaborationControlViewModel(CollaborationSessionFactory controller)
        {
            Debug.Assert(controller != null);
            this.state = CollaborationControlState.Idle;
            this.JoinCommand = new DelegateCommand<object>(param => JoinSession());
            this.HostCommand = new DelegateCommand<object>(param => HostSession());
            this.LeaveCommand = new DelegateCommand<object>(param => LeaveSession());
            this.NavigateToCommand = new DelegateCommand<string>(param => this.session.GoToFile(param));
            this.PrintDebugCommand = new DelegateCommand<object>(param => {
                SaveFileDialog dialog = new SaveFileDialog();
                dialog.Filter = "xml files (*.xml)|*.xml";
                if(dialog.ShowDialog() == DialogResult.OK){
                    Collab.Modules.Editing.UnitTestBuilder.PrintDebug(dialog.OpenFile());
                }

                
            });
            this.sessionFactory = controller;
            this.users = new ObservableCollection<User>();
            this.ip = "[None]";
            this.CanLeave = false;
            this.CanHost = true;
            this.CanJoin = true;

            Messenger.Default.OfType<JoinMessage>().ObserveOnDispatcher().Subscribe(x => this.DoJoinSession(x.UserName, x.Ip));
            Messenger.Default.OfType<HostMessage>().ObserveOnDispatcher().Subscribe(x => this.DoHostSession(x.UserName));
        }

        
        private void JoinSession()
        {
            JoinView joinView = new JoinView { Owner = System.Windows.Application.Current.MainWindow };
            if (joinView.ShowDialog() == true)            
                DoJoinSession(joinView.UserName.Text, joinView.Ip.Text);            
        }

        private void DoJoinSession(string username, string ip)
        {
            this.session = this.sessionFactory.JoinSession(username, ip);
            this.IP = ip;
            this.disposable.Add(session);
            this.Users = this.session.Users;
            this.state = CollaborationControlState.Following;
            this.CanJoin = false;
            this.CanHost = false;
            this.CanLeave = true;
        }

        private void HostSession()
        {
            HostView hostView = new HostView { Owner = System.Windows.Application.Current.MainWindow };
            if (hostView.ShowDialog() == true)            
                DoHostSession(hostView.UserName.Text);            
        }


        private void DoHostSession(string username)
        {
            if (this.session != null)
                LeaveSession();

            CollabDebug.UserName = username;
            this.session = this.sessionFactory.HostSession(username);
            this.disposable.Add(session);
            this.Users = this.session.Users;
            this.State = CollaborationControlState.Hosting;
            this.CanJoin = false;
            this.CanHost = false;
            this.CanLeave = true;
            this.IP = String.Format("[{0}]", IPUtility.GetTeredoIPv6Address());
        }

        private void LeaveSession()
        {
            this.session.Dispose();
            this.disposable.Remove(this.session);
            this.session = null;
            this.CanHost = true;
            this.CanJoin = true;
            this.CanLeave = false;
            this.Users.Clear();
            Messenger.Default.Send(new AllowInviteMessage());
        }

        #region IDisposable Members

        public void Dispose()
        {
            this.disposable.Dispose();
        }

        #endregion
    }
}
